{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "%matplotlib inline"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "\n",
    "# L1 Penalty and Sparsity in Logistic Regression\n",
    "\n",
    "# Logistic回归中的L1惩罚与稀疏性\n",
    "\n",
    "Comparison of the sparsity (percentage of zero coefficients) of solutions when L1 and L2 penalty are used for different values of C. We can see that large values of C give more freedom to the model.  Conversely, smaller values of C constrain the model more. In the L1 penalty case, this leads to sparser solutions.\n",
    "  \n",
    "We classify 8x8 images of digits into two classes: 0-4 against 5-9. The visualization shows coefficients of the models for varying C. \n",
    "  \n",
    "比较在L1和L2惩罚函数不同C值时，解的稀疏程度（系数为0的百分比），我们可以看到当C值越大时，模型的正则化强度越大，但相反，当C值越小时，模型的正则化强度越小，模型的约束程度越大。相同的C值下L1惩罚下，解的稀疏程度会高。  \n",
    "  \n",
    "我们将8x8的手写数字图片分为两类：0-4和5-9。通过可视化展现不同C值下的模型参数。\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Automatically created module for IPython interactive environment\n"
     ]
    }
   ],
   "source": [
    "print(__doc__)\n",
    "\n",
    "# Authors: Alexandre Gramfort <alexandre.gramfort@inria.fr>\n",
    "#          Mathieu Blondel <mathieu@mblondel.org>\n",
    "#          Andreas Mueller <amueller@ais.uni-bonn.de>\n",
    "# License: BSD 3 clause\n",
    "\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "from sklearn.linear_model import LogisticRegression\n",
    "from sklearn import datasets\n",
    "from sklearn.preprocessing import StandardScaler"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 加载手写数字数据集\n",
    "digits = datasets.load_digits()\n",
    "\n",
    "# 分别给数据集和标签集赋值X, y\n",
    "X, y = digits.data, digits.target\n",
    "# 将数据集标准化\n",
    "X = StandardScaler().fit_transform(X)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "# classify small against large digits\n",
    "# 将标签集分为两类0-4一类，5-9一类\n",
    "y = (y > 4).astype(np.int)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "当C=1.00时\n",
      "L1惩罚项得到的参数稀疏度: 4.69%\n",
      "L1惩罚项的模型性能: 0.9087\n",
      "L2惩罚项得到的参数稀疏度: 4.69%\n",
      "L2惩罚项的模型性能: 0.9037\n",
      "当C=0.10时\n",
      "L1惩罚项得到的参数稀疏度: 26.56%\n",
      "L1惩罚项的模型性能: 0.9009\n",
      "L2惩罚项得到的参数稀疏度: 4.69%\n",
      "L2惩罚项的模型性能: 0.9021\n",
      "当C=0.01时\n",
      "L1惩罚项得到的参数稀疏度: 84.38%\n",
      "L1惩罚项的模型性能: 0.8620\n",
      "L2惩罚项得到的参数稀疏度: 4.69%\n",
      "L2惩罚项的模型性能: 0.8898\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 6 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Set regularization parameter\n",
    "# 取不同的C值，分别取1，0.1，0.01\n",
    "for i, C in enumerate((1, 0.1, 0.01)):\n",
    "    # turn down tolerance for short training time\n",
    "    # 根据C值得到L1惩罚模型和L2惩罚模型\n",
    "    clf_l1_LR = LogisticRegression(C=C, penalty='l1', tol=0.01, solver='saga')\n",
    "    clf_l2_LR = LogisticRegression(C=C, penalty='l2', tol=0.01, solver='saga')\n",
    "    clf_l1_LR.fit(X, y)\n",
    "    clf_l2_LR.fit(X, y)\n",
    "    \n",
    "    # LR模型的参数向量\n",
    "    coef_l1_LR = clf_l1_LR.coef_.ravel()\n",
    "    coef_l2_LR = clf_l2_LR.coef_.ravel()\n",
    "\n",
    "    # coef_l1_LR contains zeros due to the L1 sparsity inducing norm\n",
    "    # 计算L1和L2惩罚下，模型参数w的稀疏度\n",
    "    sparsity_l1_LR = np.mean(coef_l1_LR == 0) * 100\n",
    "    sparsity_l2_LR = np.mean(coef_l2_LR == 0) * 100\n",
    "    \n",
    "    print(\"当C=%.2f时\" % C)\n",
    "    #print(\"Sparsity with L1 penalty: %.2f%%\" % sparsity_l1_LR)\n",
    "    #print(\"score with L1 penalty: %.4f\" % clf_l1_LR.score(X, y))\n",
    "    #print(\"Sparsity with L2 penalty: %.2f%%\" % sparsity_l2_LR)\n",
    "    #print(\"score with L2 penalty: %.4f\" % clf_l2_LR.score(X, y))\n",
    "    print(\"L1惩罚项得到的参数稀疏度: %.2f%%\" % sparsity_l1_LR)\n",
    "    print(\"L1惩罚项的模型性能: %.4f\" % clf_l1_LR.score(X, y))\n",
    "    print(\"L2惩罚项得到的参数稀疏度: %.2f%%\" % sparsity_l2_LR)\n",
    "    print(\"L2惩罚项的模型性能: %.4f\" % clf_l2_LR.score(X, y))\n",
    "    \n",
    "    # 设置图形展示位置\n",
    "    l1_plot = plt.subplot(3, 2, 2 * i + 1)\n",
    "    l2_plot = plt.subplot(3, 2, 2 * (i + 1))\n",
    "    if i == 0:\n",
    "        l1_plot.set_title(\"L1 penalty\")\n",
    "        l2_plot.set_title(\"L2 penalty\")\n",
    "    \n",
    "    # 展示图片，设置8*8的位图展示\n",
    "    l1_plot.imshow(np.abs(coef_l1_LR.reshape(8, 8)), interpolation='nearest',\n",
    "                   cmap='binary', vmax=1, vmin=0)\n",
    "    l2_plot.imshow(np.abs(coef_l2_LR.reshape(8, 8)), interpolation='nearest',\n",
    "                   cmap='binary', vmax=1, vmin=0)\n",
    "    # 设置C值的展示位置\n",
    "    plt.text(-11, 4, \"C = %.2f\" % C)\n",
    "    \n",
    "    # 不展示任何坐标轴的样式和数据\n",
    "    l1_plot.set_xticks(())\n",
    "    l1_plot.set_yticks(())\n",
    "    l2_plot.set_xticks(())\n",
    "    l2_plot.set_yticks(())\n",
    "\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.2"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": false,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
